package com.tjhk.netty.util;

import io.netty.buffer.ByteBuf;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

/**
 * BCD编码工具类
 * @author yezhihao
 * home https://gitee.com/yezhihao/jt808-server
 */
public class Bcd {

    public static final int YEAR = LocalDate.now().getYear();
    public static final int YEAR_RANGE = YEAR - 30;
    public static final int HUNDRED_YEAR = YEAR_RANGE / 100 * 100;

    /** BCD转String */
    public static String toString(byte[] bcd) {
        return new String(toChars(bcd));
    }

    /** BCD转char[] */
    public static char[] toChars(byte[] bcd) {
        char[] chars = new char[bcd.length * 2];
        for (int i = 0, j = 0; i < bcd.length; i++) {
            chars[j++] = (char) (48 + (bcd[i] >> 4 & 0xf));
            chars[j++] = (char) (48 + (bcd[i] & 0xf));
        }
        return chars;
    }

    /** String转BCD */
    public static byte[] from(String str) {
        return from(str.toCharArray());
    }

    /** char[]转BCD */
    public static byte[] from(char[] chars) {
        byte[] bcd = new byte[chars.length / 2];
        for (int i = 0, j = 0; i < bcd.length; i++) {
            bcd[i] = (byte) ((chars[j++] - 48 << 4) | ((chars[j++] - 48 & 0xf)));
        }
        return bcd;
    }
    /** 写入3字节时间(HHmmss) */
    public final void writeTime3(ByteBuf output, LocalTime time) {
        output.writeByte(toByte(time.getHour())).writeByte(toByte(time.getMinute())).writeByte(toByte(time.getSecond()));
    }

    /** 读取3字节时间(HHmmss) */
    public final LocalTime readTime3(ByteBuf input) {
        int hour = toInt(input.readByte());
        int minute = toInt(input.readByte());
        int second = toInt(input.readByte());
        try {
            return LocalTime.of(hour, minute, second);
        } catch (Exception e) {
            return null;
        }
    }
    public static int getYear(int year) {
        year += HUNDRED_YEAR;
        if (year < YEAR_RANGE)
            year += 100;
        return year;
    }


    /** 写入3字节日期(yyMMdd) */
    public final void writeDate3(ByteBuf output, LocalDate time) {
        output.writeByte(toByte(time.getYear() % 100)).writeByte(toByte(time.getMonthValue())).writeByte(toByte(time.getDayOfMonth()));
    }

    /** 读取3字节日期(yyMMdd) */
    public final LocalDate readDate3(ByteBuf input) {
        int year = getYear(toInt(input.readByte()));
        int month = toInt(input.readByte());
        int dayOfMonth = toInt(input.readByte());
        try {
            return LocalDate.of(year, month, dayOfMonth);
        } catch (Exception e) {
            return null;
        }
    }


    /** 写入6字节时间(yyMMddHHmmss) */
    public final void writeDateTime6(ByteBuf output, LocalDateTime dateTime) {
        writeDate3(output, dateTime.toLocalDate());
        writeTime3(output, dateTime.toLocalTime());
    }

    /** 读取6字节时间(yyMMdddHHmmss) */
    public final LocalDateTime readDateTime6(ByteBuf input) {
        LocalDate date = readDate3(input);
        LocalTime time = readTime3(input);
        if (date == null || time == null)
            return null;
        return LocalDateTime.of(date, time);
    }


    /** 时间转BCD (yyMMddHHmmss) */
//    public static byte[] from(LocalDateTime dateTime) {
//        byte[] bcd = new byte[6];
//        bcd[0] = bcd(dateTime.getYear() % 100);
//        bcd[1] = bcd(dateTime.getMonthValue());
//        bcd[2] = bcd(dateTime.getDayOfMonth());
//        bcd[3] = bcd(dateTime.getHour());
//        bcd[4] = bcd(dateTime.getMinute());
//        bcd[5] = bcd(dateTime.getSecond());
//        return bcd;
//    }
    public static byte[] from(LocalDateTime dateTime) {
        byte[] bcd = new byte[6];
        bcd[0] = (byte) (dateTime.getYear() % 100);
        bcd[1] = (byte) dateTime.getMonthValue();
        bcd[2] = (byte)(dateTime.getDayOfMonth());
        bcd[3] = (byte)(dateTime.getHour());
        bcd[4] = (byte)(dateTime.getMinute());
        bcd[5] = (byte)(dateTime.getSecond());
        return bcd;
    }
    /** BCD转时间 (yyMMddHHmmss) */
//    public static LocalDateTime toDateTime(byte[] bcd) {
//        int i = bcd.length - 1;
//        int year = HUNDRED_YEAR + num(bcd[i - 5]);
//        if (year < YEAR_RANGE)
//            year += 100;
//        try {
//            return LocalDateTime.of(
//                    year,
//                    num(bcd[i - 4]),
//                    num(bcd[i - 3]),
//                    num(bcd[i - 2]),
//                    num(bcd[i - 1]),
//                    num(bcd[i]));
//        } catch (Exception e) {
//            return null;
//        }
//    }
    /** BCD转时间 (yyMMddHHmmss) */
    public static LocalDateTime toDateTime(byte[] bcd) {
        int i = bcd.length - 1;
        int year = 2000 + bcd[i - 5];
        try {
            return LocalDateTime.of(
                    year,
                    bcd[i - 4],
                    bcd[i - 3],
                    bcd[i - 2],
                    bcd[i - 1],
                    bcd[i]);
        } catch (Exception e) {
            return null;
        }
    }
    /** 日期转BCD (yyMMdd) */
    public static byte[] from(LocalDate date) {
        byte[] bcd = new byte[3];
        bcd[0] = bcd(date.getYear() % 100);
        bcd[1] = bcd(date.getMonthValue());
        bcd[2] = bcd(date.getDayOfMonth());
        return bcd;
    }

    /** BCD转日期 (yyMMdd) */
    public static LocalDate toDate(byte[] bcd) {
        int i = bcd.length - 1;
        int year = HUNDRED_YEAR + num(bcd[i - 2]);
        if (year < YEAR_RANGE)
            year += 100;
        return LocalDate.of(year, num(bcd[i - 1]), num(bcd[i]));
    }

    /** BCD转时间 (HHMM) */
    public static LocalTime readTime2(ByteBuf input) {
        return LocalTime.of(num(input.readByte()), num(input.readByte()));
    }

    /** BCD转时间 (HHMM) */
    public static void writeTime2(ByteBuf output, LocalTime time) {
        output.writeByte(bcd(time.getHour()));
        output.writeByte(bcd(time.getMinute()));
    }

    public static byte bcd(int num) {
        return (byte) ((num / 10 << 4) | (num % 10 & 0xf));
    }

    public static int num(byte bcd) {
        return (bcd >> 4 & 0xf) * 10 + (bcd & 0xf);
    }

    public static int indexOf(char[] chars, char pad) {
        int i = 0, len = chars.length;
        while (i < len && chars[i] == pad) i++;
        return i;
    }

    public byte toByte(int i) {
        return (byte) i;
    }

    public int toInt(byte b) {
        return b & 0xff;
    }

}